﻿using System;

namespace CSharpHelpers.Prints
{
    class BarcodeHelper
    {
        //**********************************************************************************//
        //生成CRC的低位和高位码
        private static void breakShort(int src, out char highbyte, out char lowerbyte)
        {
            lowerbyte = (char)(src & 0xff);
            highbyte = (char)(src >> 8);
        }

        //生成CRC码
        private static int CRC(string s, int len, int crcval)
        {
            int c, q;
            for (int Index = 0; Index < len; Index++)
            {
                c = (int)s[Index];
                q = (crcval ^ c) & 15;
                crcval = (crcval >> 4) ^ (q * 4225);
                q = (crcval ^ (c >> 4)) & 15;
                crcval = (crcval >> 4) ^ (q * 4225);
            }
            return crcval;
        }

        //************************************************************************************//

        /// <summary>
        /// Step2:Array A gets transformed into array B
        /// </summary>
        /// <param name="BinStr"></param>
        /// <returns></returns>
        private static string AToB(string Astr)
        {
            string Bstr = string.Empty;

            Bstr += (char)((int)Astr[0] ^ (int)Astr[4]); //B5 = A5 ^ A1
            Bstr += (char)((int)Astr[1] ^ (int)Astr[5]); //B4 = A4 ^ A0
            Bstr += (char)((int)Astr[2] ^ (int)Astr[4]); //B3 = A3 ^ A1
            Bstr += (char)((int)Astr[3] ^ (int)Astr[5]); //B2 = A2 ^ A0
            Bstr += Astr[4];                             //B1
            Bstr += Astr[5];                             //B0

            return Bstr;
        }

        /// <summary>
        /// Step3:Array B gets transformed into array C
        /// </summary>
        /// <param name="Bstr"></param>
        /// <returns></returns>
        private static string BToC(string Bstr)
        {
            string Cstr = string.Empty;
            int crcval = 0;
            char hig, low;
            string str;

            for (int index = 0; index < Bstr.Length; index += 2)
            {
                str = string.Empty;
                str += Bstr[index + 1];
                str += Bstr[index];
                crcval = CRC(str, str.Length, 0);
                breakShort(crcval, out hig, out low);
                Cstr += hig;
                Cstr += low;
            }

            return Cstr;
        }

        /// <summary>
        /// Step4:Binary to BCD
        /// </summary>
        /// <param name="BinStr"></param>
        /// <returns></returns>
        private static string BinaryToBCD(string BinStr)
        {
            string BCDStr = string.Empty;
            string str = string.Empty;

            foreach (char subStr in BinStr)
            {
                str += ((int)subStr).ToString("X2");
            }

            BCDStr = Convert.ToInt32(str, 16).ToString("D8");

            return BCDStr;
        }

        /// <summary>
        /// Step5:
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static string NToV(string Nstr)
        {
            string Vstr = string.Empty;
            int num, sum, N7, N15, V7, V15;

            sum = 0;
            for (int i = 0; i <= 7; i++)
            {
                num = Convert.ToInt32(Nstr[i].ToString(), 10);
                sum += num;
            }
            N15 = Convert.ToInt32(Nstr[0].ToString(), 10);
            V15 = N15 | ((sum % 5) << 1);

            Vstr += V15.ToString("D");

            Vstr += Nstr.Substring(1, 7);

            sum = 0;
            for (int k = 8; k <= 15; k++)
            {
                num = Convert.ToInt32(Nstr[k].ToString(), 10);
                sum += num;
            }
            N7 = Convert.ToInt32(Nstr[8].ToString(), 10);
            V7 = N7 | ((sum % 5) << 1);

            Vstr += V7.ToString("D");

            Vstr += Nstr.Substring(9, 7);

            return Vstr;
        }

        private static string StringToHex(string str)
        {
            string BCDstr = string.Empty;

            for (int i = 0; i < str.Length; i += 2)
            {
                BCDstr += (char)Convert.ToInt32(str.Substring(i, 2), 16);
            }

            return BCDstr;
        }

        /// <summary>
        /// 传入两个string分别为MachineID和Sequence Number，返回唯一的Validation Number
        /// 例如:
        /// MachineID: 0x654321
        /// Sequence Number: 0x000001
        /// 只需要传入对应的传入"654321"和"000001"便可
        /// </summary>
        /// <param name="MachineID"></param>
        /// <param name="SequenceNumber"></param>
        /// <returns></returns>
        public static string GetValidationNumber(string MachineID, string SequenceNumber)
        {
            //step1
            string substr = string.Empty;
            string Validation = string.Empty;
            string str = MachineID;
            str += SequenceNumber;

            str = StringToHex(str);

            str = AToB(str);//step2
            str = BToC(str);//step3

            //step4
            substr = BinaryToBCD(str.Substring(0, 3));
            Validation += substr;
            substr = BinaryToBCD(str.Substring(3, 3));
            Validation += substr;

            //step5
            Validation = NToV(Validation);

            return Validation;
        }

        public static string GetValidationNumber(string SequenceNumber)
        {
            string substr = string.Empty;
            string beValidate = string.Empty;
            string Validation = string.Empty;
            SequenceNumber = StringToHex(SequenceNumber);

            SequenceNumber = AToB(SequenceNumber);//step2
            SequenceNumber = BToC(SequenceNumber);//step3

            //step4
            substr = BinaryToBCD(SequenceNumber.Substring(0, 3));
            Validation += substr;
            substr = BinaryToBCD(SequenceNumber.Substring(3, 3));
            Validation += substr;

            //step5
            Validation = NToV(Validation);

            Random rand = new Random();
            beValidate = rand.Next(0, 255).ToString("X12");
            Validation = beValidate.Substring(beValidate.Length - 2, 2) + Validation;

            return Validation;
        }
    }
}
